﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using snat.model;

namespace algorithms.utils.metrics.network
{
    public class ConnectedComponents
    {
        public static Dictionary<int, HashSet<ClusterNode>> EdgeDelineatedClusters(Dictionary<Edge, int> betweeness, int minclust, int threshold = 2) {
            List<Edge> orderededges=betweeness.OrderByDescending(e => e.Value).ToList().ConvertAll(e=>e.Key);
            List<Edge> blocklist = new List<Edge>();
            Dictionary<int, HashSet<ClusterNode>> clustering;

            clustering = EdgeBlockedConnectedComponents(blocklist);

            int edge = 0;
            while (clustering.Count < minclust && blocklist.Count < orderededges.Count) {
                blocklist.Add(orderededges[edge++]);
                clustering = EdgeBlockedConnectedComponents(blocklist, threshold);

            }
            return clustering;
        }

        private static Dictionary<int, HashSet<ClusterNode>> EdgeBlockedConnectedComponents(IEnumerable<Edge> blocklist, int threshold = 2) {
            HashSet<Edge> blocks = new HashSet<Edge>(blocklist);
            Dictionary<int, HashSet<ClusterNode>> clustering = new Dictionary<int, HashSet<ClusterNode>>();
            //todo remove
            HashSet<ClusterNode> population = new HashSet<ClusterNode>(NetworkModel.Instance.Nodes.ToList().ConvertAll(kvp => (ClusterNode)kvp.Value));

            Queue<ClusterNode> remaining = new Queue<ClusterNode>();

            int clusterid = 1;
            while (population.Count > 0) {
                HashSet<ClusterNode> thiscluster = new HashSet<ClusterNode>();
                ClusterNode current = population.First();
                population.Remove(current);
                remaining.Enqueue(current);

                while (remaining.Count > 0) {
                    current = remaining.Dequeue();
                    List<Edge> connections = current.OverlayEdges;
                    thiscluster.Add(current);
                    foreach (Edge e in connections) {
                        if (blocks.Contains(e)) continue;
                        ClusterNode from = (ClusterNode)e.Source;
                        ClusterNode to = (ClusterNode)e.Target;
                        if (population.Contains(from)) {
                            population.Remove(from);
                            remaining.Enqueue(from);
                        }
                        if (population.Contains(to)) {
                            population.Remove(to);
                            remaining.Enqueue(to);
                        }
                    }
                }
                clustering.Add(clusterid++, thiscluster);
            }


            HashSet<ClusterNode> uncluster = new HashSet<ClusterNode>();
            foreach (int cluster in clustering.Keys.AsEnumerable().ToList()) {
                if (clustering[cluster].Count < threshold) {
                    uncluster.UnionWith(clustering[cluster]);
                    clustering.Remove(cluster);
                }
            }




            Dictionary<int, HashSet<ClusterNode>> result = new Dictionary<int, HashSet<ClusterNode>>();
            int cur = 1;
            foreach (KeyValuePair<int, HashSet<ClusterNode>> kvp in clustering.OrderByDescending(kvp => kvp.Value.Count)) {
                result.Add(cur++, kvp.Value);
            }
            result.Add(0, uncluster);

          



            return result;
        }
    }
}
